﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Normalify
{
	class Program
	{
		private static readonly IDictionary<string, Type> SupportedTypes = new Dictionary<string, Type>
            {
                {"Int", typeof (int?)},
                {"Double", typeof (double?)},
                {"DateTime", typeof (DateTime?)},
                {"String", typeof (string)}
            };

		static void Main(string[] args)
		{
			var fileName = args[0];
			var typesFile = string.Format("{0}.types.txt", fileName);
			var facts = string.Format("{0}.facts.txt", fileName);
			var dims = string.Format("{0}.dims.txt", fileName);
			var outFile = string.Format("{0}.out.txt", fileName);

			var typeDic = Read(typesFile, l => new { K = l.Split('=')[0], V = l.Split('=')[1] })
				.ToDictionary(kvp => kvp.K.Trim(), kvp => CreateType(kvp.V));

			var table = DataTableExtension.ReadCsv(args[0], colName => typeDic[colName]);
			var factCols = Read(facts, table.GetColumn).ToList();
			var dimClusters = ReadClustered(dims, table.GetColumn).ToList();
			var allDims = dimClusters.SelectMany(c => c.Select(table.GetName))
				.Distinct()
				.Select(table.GetColumn)
				.ToList();

			using (var outF = File.CreateText(outFile))
			{
					outF.Write(" Fact\t Zeros\t Min\t Max\t Sum\t Mean\t Count\t Variance\t StDev\t Skewness\t Kurtosis");
					allDims.ForEach(c => outF.Write("\t {0}", table.GetName(c)));
					outF.WriteLine();

				foreach (var dimCols in dimClusters)
				{
					var brx = new BusinessRuleExtractor();
					brx.Load(table);
					brx.Initialize(factCols, dimCols);


					brx.OnBeforeReset = factCol =>
						{
							PrintBusinessRules(outF, table.GetName(factCol), table, brx.GetRules(factCol), allDims);
							outF.Flush();
						};

					brx.Execute(true);
				}
				outF.Close();
			}
		}

		private static void PrintBusinessRules(StreamWriter outF, string factName, IDataTable table, BusinessRuleCollection rules, IEnumerable<IDataColumn> dimColsList)
		{
			var dimCols = dimColsList.ToDictionary(c => c.Id, table.GetName);
			foreach (var rule in rules.Rules)
			{
				//var first = true;
				var stat = rule.Value;
				outF.Write("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}\t{8}\t{9}\t{10}", factName, stat.Zeros, stat.Min, stat.Max, stat.Sum, stat.Mean, stat.Count, stat.GetVariance(), Math.Sqrt(stat.GetVariance()), stat.GetSkewness(), stat.GetKurtosis());
				var rulesDic = rule.Key.Items.ToDictionary(k => k.Pair.Item1.Id, k => k.Pair.Item2);

				foreach (var col in dimCols.Keys)
				{
					outF.Write("\t{0}", rulesDic.ContainsKey(col) ? rulesDic[col] : string.Empty);
				}
				//foreach (var item in rule.Key.Items)
				//{
				//	//outF.Write(first ? "" : @" & ");
				//	//outF.Write("{0} = {2}{1}{2}", table.GetName(item.Pair.Item1), item.Pair.Item2, item.Pair.Item2 is string ? "'" : "");

				//	first = false;
				//}
				outF.WriteLine();
			}
		}

		private static Type CreateType(string s)
		{
			return SupportedTypes[s.Trim()];
		}

		private static IEnumerable<T> Read<T>(string s, Func<string, T> parser)
		{
			return ReadClustered(s, parser).First();
		}

		private static IEnumerable<List<T>> ReadClustered<T>(string s, Func<string, T> parser)
		{
			const string seperator = "---";
			var cluster = new List<string>();
			using (var file = File.OpenText(s))
			{
				while (!file.EndOfStream)
				{
					var line = file.ReadLine();
					if (string.IsNullOrWhiteSpace(line))
						continue;
					if (line.StartsWith(seperator))
					{
						yield return cluster.Select(parser).ToList();
						cluster.Clear();
						continue;
					}
					cluster.Add(line);
				}
				if (cluster.Any())
					yield return cluster.Select(parser).ToList();
				file.Close();
			}
		}
	}
}
